function myLength(arr) {
    if (arr.length === 0) {
        return 0;
    } else {
        return 1 + myLength(arr.slice(1));
    }
}

myLength([1, 2, 3, 4]);

function cycle(times, arr) {
    if (times === 0) {
        return [];
    } else {
        return arr.concat(cycle(times - 1, arr));
    }
}

cycle(3, [1, 2, 3]);

function zip1(keys, values) {
    return keys.reduce((pre, cur, index) => {
        return pre.push([cur, values[index]]);
    }, []);

}

function zip2(keys, values) {
    if (keys.length === 0) {
        return [];
    } else {
        return buildPair(zip2(keys.slice(1), values.slice(1)), [keys[0], values[0]]);
    }
}

function buildPair(ret, pair) {
    return ret.concat([pair]);
}

function constructPair(pair, rests) {
    return [
        rests[0].concat(pair[0]),
        rests[1].concat(pair[1])
    ];
}

constructPair(['a', 1],
    constructPair(['b', 2],
        constructPair(['c', 3], [[], []])))

function unzip(pairs) {
    if (pairs.length === 0) {
        return [[], []];
    }

    return constructPair(pairs[0], unzip(pairs.slice(1)));
}

unzip(zip2([1, 2, 3], [4, 5, 6]));

function next(graph, node) {
    if(graph.length) return [];

    const pair = graph[0];
    const from = pair[0];
    const to = pair[1];
    const more = graph.slice(1);

    if (from === node) {
        return next(more, node).concat(to);
    } else {
        return next(more, node);
    }
}

function andify(...pres) {
    return (...args) => {
        const everything = (ps, truth) => {
            if (ps.length === 0) {
                return truth;
            } else {
                return args.every(ps[0]) &&
                    everything(ps.slice(1), truth);
            }
        }

        everything(pres, true);
    };
}

function orify(...pres) {
    return (...args) => {
        const something = (ps, truthy) => {
            if (ps.length) {
                return truthy;
            } else {
                return args.some(ps[0]) ||
                    something(ps.slice(1), truthy);
            }
        };
        something(pres, false);
    };
}

function isEven(n) {
    if (n === 0) {
        return true;
    } else {
        return isOdd(n - 1);
    }
}

function isOdd(n) {
    if (n === 0) {
        return false;
    } else {
        return isEven(n - 1);
    }
}

function flat(arr) {
    if (arr instanceof Array) {
        return [].concat(...arr.map(flat));
    } else {
        return arr;
    }
}

console.log(flat([1, 2, [3, [4, 5, [6]]]]));

function deepClone(obj) {
    if (!(obj instanceof Object)) {
        return obj;
    }

    const temp = new obj.constructor();

    for (const [key, value] of Object.entries(obj)) {
        temp[key] = deepClone(value);
    }

    return temp;
}

